"""
作者：Huayu Zong
日期：2023年01月日
"""
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import sys
from file_operation import FileOperation
from queue import Queue

UpdateTextBoxPeriod = 500    #单位ms


# 获取dict中指定key的数据
def get_data_from_dict(dict_data, key):
    # 判断dict中是否有给定的key
    if dict_data.get(key) is not None:
        return dict_data[key]
    else:
        return None

class RedictStdout():
    def __init__(self, redict_obj):
        self.save_stdout = sys.stdout
        sys.stdout = self
        self.text_box = redict_obj

    def write(self, content):
        self.text_box.insert(tk.END, content)
        self.text_box.see(tk.END)

    def restore(self):
        sys.stdout  = self.save_stdout




class App_Widgets(FileOperation):
    def __init__(self, show_msg_queue, need_period_execute_callback_list = None):
        super().__init__()  #执行子类的构造函数
        self.__main_window = None
        self.__pbar_style=None #progressBar样式
        self.__need_period_execute_callback_list = need_period_execute_callback_list
        self.__show_msg_queue = show_msg_queue

        # 组件名
        self.__text_box = None
        self.__ClearTextBox_btn = None
        self.__PrmUpdate_ProgressBar = None
        self.OTA_CAN_is_Connected_Wifi_Label = None
        self.Aliyun_is_Connected_Label = None
        self.openFile_btn = None
        self.updatePrm_btn = None

        # 构造函数中自动执行的方法
        self.__create_app()
        self.__main_window.protocol('WM_DELETE_WINDOW', self.__close_windows)  # 窗体通信协议
        self.__main_window.after(UpdateTextBoxPeriod, self.__period_refresh_ui) # 启动执行after中提供的回调函数
        pass

    # 刷新UI的回调函数
    def __period_refresh_ui(self):
        if self.__need_period_execute_callback_list:
            while not self.__show_msg_queue.empty():
                content = self.__show_msg_queue.get()

                # 执行回调函数
                for cb in self.__need_period_execute_callback_list:
                    cb(content)

        self.__main_window.after(UpdateTextBoxPeriod, self.__period_refresh_ui) # 再添加一次为了周期执行
        pass

    # 主窗体循环
    def main_window_loop(self):
        self.__main_window.mainloop()

    # 创建主窗口和添加组件
    def __create_app(self):
        self.__main_window = tk.Tk()
        self.__main_window.title('4G-OTA-CAN更新工具-目标控制器是飞思卡尔MC9S12XS128-V1')  # 标题
        self.__main_window.geometry('800x500')  # 窗口尺寸

        #创建组件
        self.__data_show_region()
        self.__status_show_region()
        self.__control_button_region()
        pass

    #数据显示区域
    def __data_show_region(self):
        #包裹组件的容器
        frm_text_box = tk.Frame(self.__main_window, width=100, height=30, borderwidth=2, relief=tk.SUNKEN)
        frm_text_box.pack()

        # 1.数据显示区域
        self.__text_box = tk.Text(frm_text_box, width=90, height=16, bg='orange', font=('宋体', 12))
        self.__text_box.pack()

        # 2.清空显示文本框
        self.__ClearTextBox_btn = tk.Button(frm_text_box, text='清空文本框', width=15, height=2, command=self.__clear_text_box)
        self.__ClearTextBox_btn.pack(side=tk.LEFT)

        # 3.程序更新进度条
        progressBar_label = tk.Label(frm_text_box, text="程序更新进度", font=('宋体', 10))
        progressBar_label.pack(side=tk.LEFT)

        self.__create_progressBar_with_percent(frm_text_box)
        self.__PrmUpdate_ProgressBar.pack(side=tk.LEFT)
        self.__PrmUpdate_ProgressBar['maximum'] = 100
        pass

    #状态提示区域
    def __status_show_region(self):
        # 包裹组件的容器
        frm_show = tk.Frame(self.__main_window, width=150, height=130, borderwidth=2, relief=tk.SUNKEN)
        frm_show.pack(side=tk.LEFT, padx=35)
        frm_show_title = tk.Label(frm_show, text='连接状态:', font=('Times New Roman', 12))
        frm_show_title.place(x=0, y=0)

        # 1.创建label用于显示OTA-CAN工具是否连接网络
        self.OTA_CAN_is_Connected_Wifi_Label = tk.Label(frm_show, width=15, height=2, text='OTA-CAN Tool', bg='red',
                                                   font=('Times New Roman', 10))
        self.OTA_CAN_is_Connected_Wifi_Label.place(x=5, y=30)

        # 2.创建label用于显示阿里云是否连接
        self.Aliyun_is_Connected_Label = tk.Label(frm_show, width=15, height=2, text='Aliyun Server', bg='red',
                                             font=('Times New Roman', 10))
        self.Aliyun_is_Connected_Label.place(x=5, y=80)
        #Aliyun_is_Connected_Label.after(10 * 000, UpdateLabel)

    #控制按钮区域
    def __control_button_region(self):
        # 操纵控件的容器
        frm = tk.Frame(self.__main_window, width=100, height=40, borderwidth=2, relief=tk.SUNKEN)
        frm.pack(side=tk.LEFT, padx=50)

        # 2.放置连接阿里云的按钮 （改进：自动连接阿里云）
        # self.connectAliyun_btn = tk.Button(frm, text='1、连接阿里云', width=15, height=2)
        # self.connectAliyun_btn.pack(side=tk.LEFT, anchor='nw')

        # 3.放置打开文件的按钮
        self.openFile_btn = tk.Button(frm, text='1、打开.S19文件', width=15, height=2)
        self.openFile_btn.pack(side=tk.LEFT, anchor='nw')

        # 4.开始更新程序的按钮
        self.updatePrm_btn = tk.Button(frm, text='2、更新程序', width=15, height=2)
        self.updatePrm_btn.pack(side=tk.LEFT, anchor='nw')

    # 创建带百分比的进度条
    def __create_progressBar_with_percent(self, pack_pos):
        self.__pbar_style = tk.ttk.Style(pack_pos)
        # add label in the layout
        self.__pbar_style.layout('text.Horizontal.TProgressbar',
                     [('Horizontal.Progressbar.trough',
                       {'children': [('Horizontal.Progressbar.pbar',
                                      {'side': 'left', 'sticky': 'ns'})],
                        'sticky': 'nswe'}),
                      ('Horizontal.Progressbar.label', {'sticky': ''})])
        # set initial text
        self.__pbar_style.configure('text.Horizontal.TProgressbar', text='0 %')
        # create progressbar
        self.__PrmUpdate_ProgressBar = tk.ttk.Progressbar(pack_pos, style='text.Horizontal.TProgressbar', length=350)

    # 设置进度条进度
    def set_progressBar_value(self, value):
        self.__PrmUpdate_ProgressBar['value'] = value # increment progressbar
        self.__pbar_style.configure('text.Horizontal.TProgressbar', text='{:g} %'.format(value))  # update label

    # 关闭窗口回调函数
    def __close_windows(self):
        a = messagebox.askquestion("询问：", "真要退出吗？")  # 询问：是，否,yes,no
        if a == 'yes':
            self.__main_window.destroy()  # 返回值为yes就退出
            sys.exit()
        pass

    # 清空文本框
    def __clear_text_box(self):
        self.__text_box['state'] = 'normal'
        self.__text_box.delete("1.0", "end")
        self.__text_box['state'] = 'disable'
        pass

    # 向文本框追加内容
    def append_text_box(self, content):
        self.__text_box['state'] = 'normal'
        self.__text_box.insert(tk.INSERT, content)
        self.__text_box.insert(tk.INSERT, '\n')
        self.__text_box['state'] = 'disable'

class App(App_Widgets):
    def __init__(self, recv_queue, send_queue):
        self.__show_msg_queue = recv_queue
        self.__send_queue = send_queue

        # 周期执行的回调函数列表
        #period_exe_callback = [self.test_hello_world, self.test_append_text_to_textbox]  #测试
        period_exe_callback = [self.__refresh_connect_Aliyun_status, self.__refresh_textbox_content,
                               self.__refresh_prm_update_percent, self.__dialog_notify_user_prm_update_status,
                               self.__continue_send_next_block_prm]
        super().__init__(self.__show_msg_queue, period_exe_callback)  # 执行子类的构造函数

        # 自动调用方法
        self.__bind_callback_for_widget()

    def __bind_callback_for_widget(self):
        self.openFile_btn['command'] = self.__open_file # 打开文件按钮
        self.updatePrm_btn['command'] = self.__update_program # 开始远程更新程序按钮



    ####################################  以下是实例方法  #######################################
    def test_hello_world(self, content):
        print(content)

    def test_append_text_to_textbox(self, content):
        self.append_text_box(content)

    # 1.刷新OTA-Tool和客户端连接阿里云的状态
    def __refresh_connect_Aliyun_status(self, msg):
        # 判断从队列中取出的元素是否为dict
        if isinstance(msg, dict):
            # 更新client阿里云连接状态
            val = get_data_from_dict(msg, 'client_connect_aliyun_status')
            if val is not None:
                self.__refrsh_client_connec_aliyun_status(val)
                return

            # 更新OTA-CAN连接阿里云状态
            val = get_data_from_dict(msg, 'OTA_CAN_ConnectAliyunState')
            if val is not None:
                self.__refrsh_ota_connec_aliyun_status(val)
                return

    # 2.将数据显示再文本框中
    def __refresh_textbox_content(self, msg):
        # 判断从队列中取出的元素是否为dict
        if isinstance(msg, dict):
            # 单块程序更新成功
            val = get_data_from_dict(msg, 'single_block_update_success')
            if val:
                self.append_text_box("block program update success")

            # 所有程序更新完毕
            val = get_data_from_dict(msg, 'program_update_complete')
            if val:
                self.append_text_box("all program update success")

            #feedback message
            val = get_data_from_dict(msg, 'FeedBackMessage')
            if val:
                self.append_text_box(msg)

    # 3.刷新程序更新进度（百分比）
    def __refresh_prm_update_percent(self, msg):
        # 判断从队列中取出的元素是否为dict
        if isinstance(msg, dict):
            # 单块程序更新成功
            val = get_data_from_dict(msg, 'single_block_update_success')
            if val:
                index = self.send_prm_block_index
                self.update_percent += len(self.fixed_size_program_list[index]) * 100 / self.file_total_size
                self.set_progressBar_value(round(self.update_percent, 0)) #四舍五入后更新到进度条

    # 4.通知用户，程序更新状态
    def __dialog_notify_user_prm_update_status(self, msg):
        # 判断从队列中取出的元素是否为dict
        if isinstance(msg, dict):
            # 所有程序更新成功
            val = get_data_from_dict(msg, 'program_update_complete')
            if val:
                a = messagebox.showinfo("提示", "远程更新程序成功！")  # 按下确定，返回'ok'
                if a == 'ok':
                    self.set_progressBar_value(0)  # 清空进度条
                    self.clear_variable_value()  # 清空文件操作中的变量

            # 程序更新失败
            val = get_data_from_dict(msg, 'program_update_failed')
            if val:
                a = messagebox.showinfo("错误", "远程更新程序失败！")  # 按下确定，返回'ok'
                if a == 'ok':
                    self.set_progressBar_value(0)  # 清空进度条
                    self.clear_variable_value()  # 清空文件操作中的变量


    # 5.收到block prm更新成功的msg，继续下一个block prm发送，直至全部发送完毕
    def __continue_send_next_block_prm(self, msg):
        # 判断从队列中取出的元素是否为dict
        if isinstance(msg, dict):
            # 单块程序更新成功
            val = get_data_from_dict(msg, 'single_block_update_success')
            if val:
                if self.send_prm_block_index == (self.program_block_count - 1):  # 发送结束
                    return

                # 准备发送下一块
                self.send_prm_block_index += 1
                self.get_single_program_block_info(self.send_prm_block_index)
                self.__send_queue.put(self.single_block_prm_info_dict)



    # 刷新client连接阿里云状态
    def __refrsh_client_connec_aliyun_status(self, val):
        if val:
            self.Aliyun_is_Connected_Label["bg"] = "green"
        else:
            self.Aliyun_is_Connected_Label["bg"] = "red"
            self.OTA_CAN_is_Connected_Wifi_Label["bg"] = "red"
        pass

    # 刷新OTA-CAN工具阿里云状态
    def __refrsh_ota_connec_aliyun_status(self, val):
        if val:
            self.OTA_CAN_is_Connected_Wifi_Label["bg"] = "green"
        else:
            self.OTA_CAN_is_Connected_Wifi_Label["bg"] = "red"
        pass

    ####################################  以下是回调函数   #######################################
    # 打开文件
    def __open_file(self):
        self.clear_variable_value()
        self.split_into_fixed_size_text()
        print("fixed size program code list:", self.fixed_size_program_list)
        print(f"program size = {self.file_total_size}")

        self.append_text_box(
            "程序文件打开成功,程序行数：{0}, 程序大小为：{1} Bytes".format(len(self.fixed_size_program_list), self.file_total_size))
        pass

    # 更新程序按钮的回调函数
    def __update_program(self):
        """
        发送第一个block prm
        :return: 无
        """
        self.__show_msg_queue.put("start to update program!")
        self.get_single_program_block_info(0)
        self.__send_queue.put(self.single_block_prm_info_dict)
        pass




def test_put_queue(queue_obj):
    queue_obj.put({'single_block_update_success': True})
    queue_obj.put({'single_block_update_success': True})
    queue_obj.put({'program_update_complete': True})


if __name__ == '__main__':
    rcv_queue = Queue()
    send_queue = Queue()

    rcv_queue.put({'client_connect_aliyun_status': True})
    rcv_queue.put({'OTA_CAN_ConnectAliyunState': True})

    rcv_queue.put({'FeedBackMessage': 'Scaning device Node ...'})
    rcv_queue.put({'FeedBackMessage': 'Scaning device Node ...'})
    rcv_queue.put({'FeedBackMessage': 'Scaning device Node ...'})
    rcv_queue.put({'FeedBackMessage': 'Fine device Node'})
    rcv_queue.put({'FeedBackMessage': 'Erase flash success!'})
    rcv_queue.put({'FeedBackMessage': 'Config flash success!'})
    #rcv_queue.put({'single_block_update_success': True})
    #rcv_queue.put({'program_update_complete': True})

    app = App(rcv_queue, send_queue)
    app.openFile_btn.after(10000, lambda:test_put_queue(rcv_queue))
    app.main_window_loop()


